<?php

namespace Common\Logic;

use Think\Exception;
use Think\Log;

/**
 * 订单退款
 */ 
class RefundLogic
{

    const USER_TABLE = "users";

    const GOODS_TABLE = "goods";

    const GROUPBUY_TABLE = "group_buy";

    const GROUP_INFO_TABLE = "group_info";

    const GROUP_ORDER_TABLE = "group_order";

    const ORDER_TABLE = "order";

    const ORDERGOODS_TABLE = "order_goods";

    const GOODS_SPEC_TABLE = "spec_goods_price";

    const REFUND_TABLE = "order_refund";

    const REFUND_LOG = "refund_record";

    private $order;

    private $refund_amount = 0;

    private $refund_reason = '';

    private $refund_sn = '';

    private $refund_record = null;

    public $user_id = 0;#用户id

    public $oper_id = 0;#操作员id

    public function getOrderInfo($order_sn = 0)
    {
        $this->order = M(self::ORDER_TABLE)->where([
            "order_sn" => $order_sn
        ])->field("order_id,order_sn,user_id,order_status,shipping_status,pay_status,pay_code,pay_name,order_amount,is_groupbuy")->find();
        
        return $this->order;
    }

    /**
     * 直接对订单号退款
     * 先创建退款单再退款
     * @param $order_sn
     * @param $amount 退款金额元或者all
     * @param $reason 退款原因
     * @return [
            "order_sn" => $this->order['order_sn'],
            "refund_sn" => $this->refund_sn,
            "amount"  => $this->refund_amount #元
        ] 或者抛出异常
     */ 
    public function createRefund($order_sn,$amount = 0,$reason = "")
    {
        if( empty($order_sn) || ( $amount!='all' && $amount <= 0) )
            throw new Exception("参数错误",-1);
        
        M()->startTrans();

        $this->getOrderInfo($order_sn);

        if( $amount == "all" )
        {
            $amount = $this->order['order_amount'];
        }
        
        if( !$this->order )
            throw new Exception("订单不存在",-1);

        if( 3 == $this->order['order_status'] || 4 == $this->order['order_status'] || 5 == $this->order['order_status'] )
            throw new Exception("订单已完成或者已取消",-1);

        if( !$this->order['pay_status'] )
            throw new Exception("订单未支付",-1);

        if( empty($this->order['pay_code']) )
            throw new Exception("未找到付款方式",-1);

        #累计退款检查
        $total_refund = M(self::REFUND_TABLE)->where([
            "order_sn" => $order_sn,
            "refund_state" => 3
        ])->sum('amount') / 100;
        
        if( bccomp( bcadd($amount,$total_refund,2) ,$this->order['order_amount'],2) > 0 )
            throw new Exception("退款金额超出订单总额",-1);
        
        #订单可退款
        $this->refund_amount = $amount;#元
        $this->refund_reason = $reason;
        $this->user_id = $this->order['user_id'];
        $this->refund_sn = self::createORderSn();

        #生成退款单
        $refund_order = $this->createRefundOrder($this->order,[
            "refund_sn" => $this->refund_sn,
            "reason" => $reason,
            "amount" => $amount * 100
        ]);

        if(!$refund_order)
            throw new Exception("退款订单生成失败，请稍后重试",-1);

        try{

            $result = $this->excuteRefund();

        }catch(\Exception $e)
        {
            M()->rollback();
            throw $e;
        }
        M()->commit();
        
        return $result; 
    }

    /**
     * 根据退款单号退款
     * @param $refund_sn 
     * @return[
            "order_sn" => $this->order['order_sn'],
            "refund_sn" => $this->refund_sn,
            "amount"  => $this->refund_amount #元
        ]或者抛出异常
     */ 
    public function refundByRefundSn($refund_sn = 0)
    {
        $refund_order = M(self::REFUND_TABLE)->where([
            "refund_sn" => $refund_sn
        ])->find();

        if(!$refund_order)
            throw new Exception("退款单号无效，请稍候重试",-1);
        
        if($refund_order['refund_state']==0)
            throw new Exception("退款单已取消",-1);
        
        if($refund_order['refund_state']==3)
            throw new Exception("退款已完成",-1);
        
        $this->getOrderInfo($refund_order['order_sn']);

        #累计退款检查
        $total_refund = M(self::REFUND_TABLE)->where([
            "order_sn" => $refund_order['order_sn'],
            "refund_state" => 3
        ])->sum('amount') / 100;
        
        if( bccomp( bcadd($amount,$total_refund,2) ,$this->order['order_amount'],2) > 0 )
            throw new Exception("退款金额超出订单总额",-1);
        

        $this->refund_amount = $refund_order['amount'] / 100;#元
        $this->refund_reason = $refund_order['reason'];
        $this->user_id = $refund_order['user_id'];
        $this->refund_sn = $refund_order['refund_sn'];

        try{

            $result = $this->excuteRefund();

        }catch(\Exception $e)
        {
            throw $e;
        }
        return $result; 
    }

    /**
     * 执行退款
     * @return [
            "order_sn" => $this->order['order_sn'],
            "refund_sn" => $this->refund_sn,
            "amount"  => $this->refund_amount #元
        ]
     **/ 
    protected function excuteRefund()
    {
        if( preg_match("/wxpay|weixin|wechat|weichat/i",$this->order['pay_code']) )
        {
            $this->order['pay_code'] = "wechat";
        }

        if( preg_match("/alipay/i",$this->order['pay_code']) )
        {
            $this->order['pay_code'] = "alipay";
        }

        $methodName = "refund".ucfirst( $this->order['pay_code'] );

        if( !is_callable( [$this,$methodName] ) )
            throw new Exception("未找到退款处理方式",-1);

        #执行退款
        $refund = call_user_func_array([$this,$methodName],[]);

        $this->log( "退款处理结果--". json_encode($refund) );

        if( !$refund )
            throw new Exception("执行退款失败，请联系技术人员",-1);
        
        $result = [
            "order_sn" => $this->order['order_sn'],
            "refund_sn" => $this->refund_sn,
            "amount"  => $this->refund_amount #元
        ];

        #团购
        if( $this->order['is_groupbuy'] )
        {
            $groupRefund = \Common\Logic\GroupbuyLogic::refundCallback($result);
            $this->log( "团购退款结果--". $groupRefund);
        }

        #修改订单状态

        /* 退款成功,修改订单状态 */
        $updateOrder = M(self::ORDER_TABLE) 
                -> where([
                    "order_sn" => $this->order['order_sn']
                ])
                -> save(['order_status' => 6]);
        
        $this->log("退款成功，修改订单状态--".$updateOrder);

        /* 退款成功,修改子订单状态 */
        $updateSubOrder = M(self::ORDER_TABLE) 
                -> where([
                    "parent_sn" => $this->order['order_sn']
                ])
                -> save(['order_status' => 6]);
        
        $this->log("修改子订单状态--".$updateSubOrder);

        /* 退款成功,修改退款订单状态 */
        $updateRec = M(self::REFUND_TABLE) 
                -> where([
                    "refund_sn" => $this->refund_sn
                ])
                -> save(['refund_state' => 3,'shop_state' => 4]);
        
        $this->log("退款成功，修改退款订单状态--".$updateRec);

        if( $this->refund_reason!='' )
        {
            $msg_con = "由于" .$this->refund_reason. ",你的订单:  ".$this->order['order_sn']." 已经原路退款!";
        }else{
            $msg_con = "你的订单 订单号:  ".$this->order['order_sn']." 已经原路退款!";
        }

        \Common\Logic\MessageLogic::sendUserMsg([
            "user_id" => $this->order['user_id'],
            "type" => \Common\Logic\MessageLogic::ORDER_MSG,
            "title" => "订单通知",
            "content" => $msg_con,
            "ext" => [
                "order_sn" => $this->order['order_sn'],
            ],
            "notice" => true
        ]);
        
        return $result;
    } 

    /**
     * 支付宝退款
     * @return bool
     */ 
    protected function refundAlipay()
    {
        if( empty($this->order) )
        { 
            return false;
        }

        try{

            vendor('Refund.AlipayTradeRefundContentBuilder');
            vendor('Refund.AlipayTradeService');

            #加载支付宝配置
            require_once "plugins/payment/alipayMobile/alipay.config.php";
            $config = [
                'app_id' => $alipay_config['app_id'],
                'merchant_private_key' => $alipay_config['private_key_value'],
                'notify_url' => $alipay_config['notify_url'],
                'charset' => $alipay_config['input_charset'],
                'sign_type' => 'RSA2',
                'gatewayUrl' => 'https://openapi.alipay.com/gateway.do',
                'alipay_public_key' => $alipay_config['rsa2_alipub_key_value'],
            ];

            $RequestBuilder = new \AlipayTradeRefundContentBuilder();
            $RequestBuilder->setOutTradeNo($this->order['order_sn']);
            $RequestBuilder->setRefundAmount($this->refund_amount);
            $RequestBuilder->setRefundReason($this->refund_reason);
            $RequestBuilder->setOutRequestNo($this->refund_sn);

            $Response = new \AlipayTradeService($config);
            $result = (array)($Response->Refund($RequestBuilder));
            
        }catch(\Exception $e)
        {
            // var_dump($e);
            $this->log("支付宝退款请求失败--order_sn-{$this->order['order_sn']}-refund_sn-{$this->refund_sn}-".$e->getCode()."-".$e->getMessage());
            return false;
        }

        $this->log("支付宝退款请求结果-order_sn-{$this->order['order_sn']}-refund_sn-{$this->refund_sn}-". json_encode($result) );
        // $this->createRefundRecord($result);
        // var_dump($result);
        if( $result ){
            #做记录
            $this->createRefundRecord( array_merge($result,[
                "refund_sn" => $this->refund_sn,
                "out_trade_no" => $this->order['order_sn'],
            ]) );
            if( $result['code'] == '10000' )
                return TRUE;
            
            $result['sub_msg'] = isset( $result['sub_msg'] ) ? $result['sub_msg']: "支付宝退款失败";
            
            throw new Exception($result['sub_msg'],-1);
        }else{
            return FALSE;
        }
        return $result;
        
    }

    /**
     * 微信退款
     * @return bool
     */ 
    protected function refundWechat()
    {
        // var_dump($this->order);
        if( empty($this->order) )
        { 
            return false;
        }
        
        try{
            #执行微信退款
            require_once "plugins/payment/weixin/lib/WxPay.Data.php";
            require_once "plugins/payment/weixin/lib/WxPay.Api.php";
            $wx_config = C( 'ZSF_WX_PAY' );
            $WxPayRefund = new \WxPayRefund;

            $WxPayRefund->SetAppid($wx_config['app_id']);
            $WxPayRefund->SetMch_id($wx_config['mch_id']);
            $WxPayRefund->SetKey($wx_config['key']);
            $WxPayRefund->SetNonce_str("2A2B2Cue9302".time());
            $WxPayRefund->SetOp_user_id( $this->oper_id ? $this->oper_id : $wx_config['mch_id'] );
            $WxPayRefund->SetOut_trade_no($this->order['order_sn']);
            $WxPayRefund->SetOut_refund_no($this->refund_sn);
            $WxPayRefund->SetTotal_fee( $this->order['order_amount'] * 100 );
            $WxPayRefund->SetRefund_fee( $this->refund_amount * 100 );

            $result = \WxPayApi::refund($WxPayRefund);
            
        }catch(Exception $e)
        {
            // var_dump($e);
            $this->log("微信退款请求失败--order_sn-{$this->order['order_sn']}-refund_sn-{$this->refund_sn}-".$e->getCode()."-".$e->getMessage());
            return false;
        }

        $this->log("微信退款请求结果-order_sn-{$this->order['order_sn']}-refund_sn-{$this->refund_sn}-". json_encode($result) );

        if( $result ){
             #做记录
            $this->createRefundRecord( array_merge($result,[
                "refund_sn" => $this->refund_sn,
                "out_trade_no" => $this->order['order_sn'],
                "code" => isset($result['return_code']) ? $result['return_code']:'',
                "msg" => isset($result['return_msg']) ? $result['return_msg'] :"",
            ]) );

            if( $result['return_code'] == 'SUCCESS' && $result['result_code'] == "SUCCESS" )
                return TRUE;
            
            $result['return_msg'] = isset( $result['err_code_des'] ) ? $result['err_code_des'] : (isset( $result['return_msg'] ) ? $result['return_msg']: "微信退款失败" );
            
            throw new Exception($result['return_msg'],-1);
        }else{
            return FALSE;
        }
        return $result;
        
    }

    /**
     * 生成退款申请单
     * @param  $order_sn
     */
    public function createRefundOrder($order_sn = null,$data = [])
    {
        if( $order_sn )
        {
            if( is_string($order_sn) )
            {
                $this->getOrderInfo($order_sn);  
            }

            if( is_array($order_sn) )
            {
                $this->order = $order_sn;
            }
        }

        if( empty($this->order) )
        {
            throw new Exception("订单信息错误",-2);
        }

        $data = array_merge($this->order,$data);

        $inertData = [];
        $record = M(self::REFUND_TABLE);
        $inertData['refund_sn'] = $data['refund_sn'];
        $inertData['order_sn'] = $data['order_sn'];
        $inertData['shop_name'] = isset($data['shop_name']) ? $data['shop_name'] :"";
        $inertData['user_id'] = isset($data['user_id']) ? $data['user_id'] :"";
        $inertData['user_name'] = isset($data['user_name']) ? $data['user_name'] :"";
        $inertData['goods_id'] = isset($data['goods_id']) ? $data['goods_id'] : 0;
        $inertData['order_goods_id'] = isset($data['order_goods_id']) ? $data['order_goods_id'] :0;
        $inertData['goods_name'] = isset($data['goods_name']) ? $data['goods_name'] :"";
        $inertData['goods_num'] = isset($data['goods_num']) ? $data['goods_num'] :"";
        $inertData['refund_type'] = isset($data['refund_type']) ? $data['refund_type'] :1;
        $inertData['amount'] = isset($data['amount']) ? $data['amount'] :0;
        $inertData['reason'] = isset($data['reason']) ? $data['reason'] :'';
        $inertData['description'] = isset($data['description']) ? $data['description'] :'';
        $inertData['images'] = isset($data['images']) ? $data['images'] :'';
        $inertData['shop_state'] = isset($data['shop_state']) ? $data['shop_state'] :1;
        $inertData['refund_state'] = isset($data['refund_state']) ? $data['refund_state'] :1;
        $inertData['add_time'] = time();
        $inertData['shop_msg'] = '';
        $inertData['admin_msg'] = '';
        $inertData['express_name'] = '';
        $inertData['express_code'] = '';
        return $record -> add($inertData);
    }

    /**
     * 生成退款订单记录
     * @param  $refund_sn              退款订单号
     * @param  $code                   网关返回码
     * @param  $msg                    网关返回码描述
     * @param  $sub_code               业务返回码
     * @param  $sub_msg                业务返回码描述
     * @param  $refund_fee             退款总金额元
     * @param  $send_back_fee          
     * @param  $buyer_logon_id         用户的登录id
     * @param  $buyer_user_id          买家在第三方的用户id/open_id
     * @param  $fund_change            本次退款是否发生了资金变化
     * @param  $gmt_refund_pay         退款支付时间
     * @param  $out_trade_no           商户订单号
     * @param  $trade_no               第三方交易号
     */
    public function createRefundRecord($data){
        $inertData = [];
        $record = M(self::REFUND_LOG);
        $inertData['refund_sn'] = isset($data['refund_sn']) ? $data['refund_sn'] : '';
        $inertData['code'] = isset($data['code']) ? $data['code'] : '';
        $inertData['msg'] = isset($data['msg']) ? $data['msg'] : '';
        $inertData['sub_code'] = isset($data['sub_code']) ? $data['sub_code'] : '';
        $inertData['sub_msg'] = isset($data['sub_msg']) ? $data['sub_msg'] : '';
        $inertData['refund_fee'] = isset($data['refund_fee']) ? $data['refund_fee'] : '';
        $inertData['send_back_fee'] = isset($data['send_back_fee']) ? $data['send_back_fee'] : '';
        $inertData['buyer_logon_id'] = isset($data['buyer_logon_id']) ? $data['buyer_logon_id'] : '';
        $inertData['buyer_user_id'] = isset($data['buyer_user_id']) ? $data['buyer_user_id'] : '';
        $inertData['fund_change'] = isset($data['fund_change']) ? $data['fund_change'] : '';
        $inertData['gmt_refund_pay'] = isset($data['gmt_refund_pay']) ? $data['gmt_refund_pay'] : '';
        $inertData['out_trade_no'] = isset($data['out_trade_no']) ? $data['out_trade_no'] : '';
        $inertData['trade_no'] = isset($data['trade_no']) ? $data['trade_no'] : '';
        return $record -> add($inertData);
    }


    protected function log($msg = "")
    {
        if( !$this->order )
            return false;
        
        if( is_array($msg) || is_object($msg) )
        {
            $msg = json_encode($msg);
        }
        
        $msg = "【{$this->order['order_sn']}】".$msg."\r\n";

        $destination = C('LOG_PATH')."refundlog_".date('y_m_d').'.log'; 

        Log::write($msg,Log::INFO,'',$destination);
    }


    protected static function getSpecs($goods_id,$key = '')
    {
        $model = M(self::GOODS_SPEC_TABLE);

        $where = [
            "goods_id" => $goods_id,
        ];

        if( !empty($key) )
        {
            $where['key']  = $key;

            return $model->where($where)->find();
        }

        return $model->where($where)->select();
    }

    protected static function randNumber($len = 4){
        $chars = str_repeat('0123456789', 10);
        $chars = str_shuffle($chars);
        $str = substr($chars, 0, $len);
        return $str;
    }

    protected static function createORderSn()
    {  
        return "R".date("YmdHis").self::randNumber(4);
    }
}